/*
** MIME Base64 coding examples
**
** encode() encodes an arbitrary data block into MIME Base64 format string
** decode() decodes a MIME Base64 format string into raw data
**
** Global table base64[] carries the MIME Base64 conversion characters
*/


/*
** Global data used by both binary-to-base64 and base64-to-binary conversions
*/

static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/*
** ENCODE RAW into BASE64
*/

/* Encode source from raw data into Base64 encoded string */
int b64encode(unsigned s_len, char *src, unsigned d_len, char *dst)
{
	unsigned triad;

	for (triad = 0; triad < s_len; triad += 3)
	{
		unsigned long int sr = 0;
		unsigned byte;

		for (byte = 0; (byte<3)&&(triad+byte<s_len); ++byte)
		{
			sr <<= 8;
			sr |= (*(src+triad+byte) & 0xff);
		}

		sr <<= (6-((8*byte)%6))%6; /*shift left to next 6bit alignment*/

		if (d_len < 4) return 1; /* error - dest too short */

		*(dst+0) = *(dst+1) = *(dst+2) = *(dst+3) = '=';
		switch(byte)
		{
			case 3:
				*(dst+3) = base64[sr&0x3f];
				sr >>= 6;
			case 2:
				*(dst+2) = base64[sr&0x3f];
				sr >>= 6;
			case 1:
				*(dst+1) = base64[sr&0x3f];
				sr >>= 6;
				*(dst+0) = base64[sr&0x3f];
		}
		dst += 4; d_len -= 4;
	}

	return 0;
}

/*
** DECODE BASE64 into RAW
*/

/* determine which sextet value a Base64 character represents */
int tlu(int byte)
{
	int index;

	for (index = 0; index < 64; ++index)
		if (base64[index] == byte)
			break;
	if (index == 63) index = -1;
	return index;
}

/*
** Decode source from Base64 encoded string into raw data
**
** Returns: 0 - Success
** 1 - Error - Source underflow - need more base64 data
** 2 - Error - Chunk contains half a byte of data
** 3 - Error - Decoded results will overflow output buffer
*/
int b64decode(unsigned s_len, char *src, unsigned d_len, char *dst)
{
	unsigned six, dix;

	dix = 0;

	for (six = 0; six < s_len; six += 4)
	{
		unsigned long sr;
		unsigned ix;

		sr = 0;
		for (ix = 0; ix < 4; ++ix)
		{
			int sextet;

			if (six+ix >= s_len)
				return 1;
			if ((sextet = tlu(*(src+six+ix))) < 0)
				break;
			sr <<= 6;
			sr |= (sextet & 0x3f);
		}

		switch (ix)
		{
			case 0: /* end of data, no padding */
				return 0;

			case 1: /* can't happen */
				return 2;

			case 2: /* 1 result byte */
				sr >>= 4;
				if (dix == d_len) return 3;
				*(dst+dix) = (sr & 0xff);
				++dix;
				break;

			case 3: /* 2 result bytes */
				sr >>= 2;
				if (dix+1 == d_len) return 3;
				*(dst+dix+1) = (sr & 0xff);
				sr >>= 8;
				*(dst+dix) = (sr & 0xff);
				dix += 2;
				break;

			case 4: /* 3 result bytes */
				if (dix+2 == d_len) return 3;
				*(dst+dix+2) = (sr & 0xff);
				sr >>= 8;
				*(dst+dix+1) = (sr & 0xff);
				sr >>= 8;
				*(dst+dix) = (sr & 0xff);
				dix += 3;
				break;
		}
	}
	return 0;
}
